Exploration Of The Data
Description of the Data
Summary of Data
summary(train)
PassengerId Survived Pclass Name Sex Age
Min. : 1.0 Min. :0.0000 Min. :1.000 Length:891 Length:891 Min. : 0.42
1st Qu.:223.5 1st Qu.:0.0000 1st Qu.:2.000 Class :character Class :character 1st Qu.:20.12
Median :446.0 Median :0.0000 Median :3.000 Mode :character Mode :character Median :28.00
Mean :446.0 Mean :0.3838 Mean :2.309 Mean :29.70
3rd Qu.:668.5 3rd Qu.:1.0000 3rd Qu.:3.000 3rd Qu.:38.00
Max. :891.0 Max. :1.0000 Max. :3.000 Max. :80.00
NA's :177
SibSp Parch Ticket Fare Cabin
Min. :0.000 Min. :0.0000 Length:891 Min. : 0.00 Length:891
1st Qu.:0.000 1st Qu.:0.0000 Class :character 1st Qu.: 7.91 Class :character
Median :0.000 Median :0.0000 Mode :character Median : 14.45 Mode :character
Mean :0.523 Mean :0.3816 Mean : 32.20
3rd Qu.:1.000 3rd Qu.:0.0000 3rd Qu.: 31.00
Max. :8.000 Max. :6.0000 Max. :512.33
Embarked
Length:891
Class :character
Mode :character
Categories of Features
Quantitative data are measures of values or counts and are expressed
as numbers.
Qualitative data are measures of ‘types’ and may be represented by a
name, symbol, or a number code.
Qualitive
Categorical: Survived, Sex, and Embarked. Ordinal: Pclass. Nominal:
Name.
Quantitive
Continuous: Age, Fare. Discrete: SibSp, Parch.
Mix types
Ticket is a mix of numeric and alphanumeric data types Cabin is mix
between alpha and numeric
Exploring Missing Data.
Checking for Missing values in each feature
colSums(is.na(train))
PassengerId Survived Pclass Name Sex Age SibSp Parch
0 0 0 0 0 177 0 0
Ticket Fare Cabin Embarked
0 0 687 2
missing_values <- train %>% summarize_all(funs(sum(is.na(.))/n()))
missing_values <- gather(missing_values, key="feature", value="missing_pct")
missing_values
missing_values %>%
ggplot(aes(x=reorder(feature,-missing_pct),y=missing_pct)) +
geom_bar(stat="identity",fill="red") +
coord_flip() + # to flip the graph
xlab("Features") +
ylab("Percentage of missing values")+
scale_y_continuous(labels=scales::percent) +
theme_ipsum()

Missing data, it is normal?
It is quite normal to see missing data in any data-set as such data
is collected by manually which means that there might be some error.
Missing data present various problems. First, the absence of data
reduces statistical power, which refers to the probability that the test
will reject the null hypothesis when it is false. Second, the lost data
can cause bias in the estimation of parameters. Third, it can reduce the
representativeness of the samples.
The solution to the missing data
Removal
The removal of the observations that contain missing might cause a
bigger issue where it might an even bigger loss of information which
cause bias in our estimation. But on close observation of our data we
can identify that there are some features that are not extremely useful
and contain missing data, so we can drop such features.
train <- train %>%
select(!Cabin)
summary(train)
PassengerId Survived Pclass Name Sex Age
Min. : 1.0 Min. :0.0000 Min. :1.000 Length:891 Length:891 Min. : 0.42
1st Qu.:223.5 1st Qu.:0.0000 1st Qu.:2.000 Class :character Class :character 1st Qu.:20.12
Median :446.0 Median :0.0000 Median :3.000 Mode :character Mode :character Median :28.00
Mean :446.0 Mean :0.3838 Mean :2.309 Mean :29.70
3rd Qu.:668.5 3rd Qu.:1.0000 3rd Qu.:3.000 3rd Qu.:38.00
Max. :891.0 Max. :1.0000 Max. :3.000 Max. :80.00
NA's :177
SibSp Parch Ticket Fare Embarked
Min. :0.000 Min. :0.0000 Length:891 Min. : 0.00 Length:891
1st Qu.:0.000 1st Qu.:0.0000 Class :character 1st Qu.: 7.91 Class :character
Median :0.000 Median :0.0000 Mode :character Median : 14.45 Mode :character
Mean :0.523 Mean :0.3816 Mean : 32.20
3rd Qu.:1.000 3rd Qu.:0.0000 3rd Qu.: 31.00
Max. :8.000 Max. :6.0000 Max. :512.33
Imputation
Imputing the missing data gives the advantage that we use a learning
model to predict such missing data and maintain the distribution of our
data.
Histogram of Age feature.
gAgeDensity <- train %>%
select(Age) %>%
ggplot(aes(Age, y = ..density..)) +
geom_histogram(bins = 20,binwidth = 1,color=inferno(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
annotate(
"text",
x = 70,
y = 0.04,
label = paste("Skewness:",skewness(train$Age,na.rm = T)),
colour = inferno(1,begin = 0.1),
size = 4
) +
theme_ipsum_rc()
gAgeDensity

Population mean and standard deviation of Age feature Before
imputation
train %>%
summarise(Age_mean = mean(Age,na.rm = T), Age_sd = sd(Age,na.rm = T))
Histogram of Fare feature
gFareDensity <- train %>%
select(Fare) %>%
ggplot(aes(Fare, y = ..density..)) +
geom_histogram(bins = 20,binwidth = 1,color=viridis(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = viridis(1,begin=0)) +
scale_y_continuous(limits = c(0,0.05))+
theme_ipsum_rc() +
annotate(
"text",
x = 200,
y = 0.05,
label = paste("Skewness",skewness(train$Fare)),
colour = "black",
size = 4
)
gFareDensity

Population mean and standard deviaiton of Fare feature.
train %>%
summarise(Fare_mean = mean(Fare,na.rm = T), Fare_sd = sd(Fare,na.rm = T))
Imputing the missing age feature
#---------------MICE--------------
set.seed(129)
mice_mod <- mice(train[,!names(train) %in% c('PassengerId','Name','Ticket','Cabin','Survived')],method = 'rf')
iter imp variable
1 1 Age
1 2 Age
1 3 Age
1 4 Age
1 5 Age
2 1 Age
2 2 Age
2 3 Age
2 4 Age
2 5 Age
3 1 Age
3 2 Age
3 3 Age
3 4 Age
3 5 Age
4 1 Age
4 2 Age
4 3 Age
4 4 Age
4 5 Age
5 1 Age
5 2 Age
5 3 Age
5 4 Age
5 5 Age
Warning: Number of logged events: 2
mice_output <- complete(mice_mod)
gdistrOriginalData <- train %>%
select(Age) %>%
ggplot(aes(Age, y = ..density..)) +
geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
ggtitle("Distribution of original data") +
theme_ipsum_rc()
gdistrMICEData <- mice_output %>%
select(Age) %>%
ggplot(aes(Age, y = ..density..)) +
geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
ggtitle("Distribution of mice output") +
theme_ipsum_rc()
gridExtra::grid.arrange(gdistrOriginalData,gdistrMICEData,nrow = 1)

train$Age <- mice_output$Age
missing_values <- train %>% summarize_all(funs(sum(is.na(.))/n()))
missing_values <- gather(missing_values, key="feature", value="missing_pct")
missing_values
missing_values %>%
ggplot(aes(x=reorder(feature,-missing_pct),y=missing_pct)) +
geom_bar(stat="identity",fill="red") +
coord_flip() + # to flip the graph
xlab("Features") +
ylab("Percentage of missing values")+
scale_y_continuous(labels=scales::percent) +
theme_ipsum()

Population mean and standard deviation
train %>%
summarise(Age_mean = mean(Age,na.rm = T), Age_sd = sd(Age,na.rm = T))
Determining the distrubutuion of Age and Fare By inspection
Though determination of the distribution using inspection is likely
not going to be effective we are going to the KS-test in a later
section
Age
The histogram of the Age feature look very much like a normal
distribution, yet it’s not a normal distribution itself.
Fare
The histogram of the Fare feature fits the \(\chi^2\) distribution.
Plotting The Data
We include even more plots of our data to get a better understanding
of it, help us see hidden correlations and ways to facilitate our
analysis
Correlation Matrix
We are going to use correlation matrix of the numerical data to
assess the correlation, which might gives a better idea of which feature
might be important
correlationMatrix <- train %>%
filter(!is.na(Age)) %>%
select(Survived, Pclass,Age,SibSp,Parch,Fare) %>%
cor() %>%
ggcorrplot(lab = T,
ggtheme =theme_ipsum_rc(grid = F),
title="Correlation Matrix",hc.order=T,
colors =rev(viridis(3,alpha=0.7)),
digits = 1)
correlationMatrix

The fare features seems to be the most correlated feature to survival
of the passengers, but it doesn’t negate the importance of the other
features in the data. Which means that we will start by comparing the
each that we consider to be important against survival feature
Class of Passenger Vs Survived
gPclassSurvived <- train %>%
select(Pclass,Survived) %>%
ggplot(aes(as_factor(Pclass),fill=as_factor(Survived))) +
geom_bar(position = "fill") +
scale_y_continuous(labels=scales::percent) +
theme_ipsum_rc() +
labs(x = "Classes",y = "Survival Rate")+
scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived"))
gPclassSurvived

From this Plot, it seems clear that people from the upper classes had
higher survival rates, thought this seemed obvious from the beginning.
### Siblings and Spouses Vs Survived
gSibSpSurvived <- train %>%
select(SibSp,Survived) %>%
ggplot(aes(as_factor(SibSp),fill=as_factor(Survived))) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent) +
labs(x = "Siblings and Spouses",y = "Survival Rate")+
scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
theme_ipsum()
gSibSpSurvived

This plot highlight a point that might be counter-intuitive that
people that have no siblings or spouses had lower survival rates than
those with at least one sibling or a spouse
Number of children/parents Vs Survived
gParchSurvived <- train %>%
select(Parch,Survived) %>%
ggplot(aes(as_factor(Parch),fill=as_factor(Survived))) +
geom_bar(position = "fill") +
scale_y_continuous(label = scales::percent)+
labs(x = "Number of parents/children",y = "Survival Rate")+
scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
theme_ipsum_rc()
gParchSurvived

Gender VS Survived
gSexSurvived <- train %>%
select(Sex,Survived) %>%
ggplot(aes(as_factor(Sex),fill = as_factor(Survived))) +
geom_bar(position = "fill") +
scale_y_continuous(label = scales::percent) +
labs(x = "Sex",y = "Survival Rate")+
scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
theme_ipsum_rc()
print(gSexSurvived)

Survival Density and Age
gSurvivalAgeDensity <- ggplot(train[(!is.na(train$Survived) & !is.na(train$Age)),],
aes(x = Age,
fill = Survived)) +
geom_density(alpha=0.5,
aes(fill=as_factor(Survived))) +
labs(title="Survival density and Age") +
scale_x_continuous(breaks = scales::pretty_breaks(n = 10)) +
theme_ipsum()
gSurvivalAgeDensity

Dashboard of the previous graphs
gridExtra::grid.arrange(gPclassSurvived,
gSibSpSurvived,
gSexSurvived,
gParchSurvived,
nrow=2)

train %>%
group_by(Sex) %>%
summarise(Age_mean = mean(Age,na.rm=TRUE),
age_sd = sd(Age,na.rm=T),
surival_mean = mean(Survived,na.rm =T),
surival_sd = sd(Survived,na.rm = T))
NA
Sampling
Random sample of size of 50 (Age)
sample_50 <- sample_n(train,size = 50,replace = T) %>%
summarise(mean = mean(Age), sd = sd(Age))
sample_50
Sampling distrubution with a fixed size (50) (mean)
numOfSamples <- seq(50,1000,50)
smplngDstrbtnRpsChng <- tibble()
for(i in numOfSamples){
for(y in 1:i){
nsample <- sample_n(train,size=50,replace=T) %>%
select(Age)
newRow <- nrow(smplngDstrbtnRpsChng) + 1
smplngDstrbtnRpsChng[newRow,"reps"] <- i
smplngDstrbtnRpsChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
}
}
gSamplingReps <- smplngDstrbtnRpsChng %>%
plot_ly(
x = ~x_bar,
frame = ~reps,
type = "histogram"
)
gSamplingReps
While no. of sample size increase the variability of sampling
distribution decrease and the mean increase.
Sampling distribution with a fixed repetitions and different sizes
(mean)
sizes <- seq(20,260,20)
smplngDstrbtnSzChng <- tibble()
for(i in sizes){
for(y in 1:1500){
nsample <- sample_n(train,size=i,replace=T) %>%
select(Age)
newRow <- nrow(smplngDstrbtnSzChng) + 1
smplngDstrbtnSzChng[newRow,"sizes"] <- i
smplngDstrbtnSzChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
}
}
gSamplingSize <- smplngDstrbtnSzChng %>%
plot_ly(
x = ~x_bar,
frame = ~sizes,
type = "histogram"
)
gSamplingSize
While no. of sample size increase the variability of sampling
distribution decrease,and The sample distribution mean will be normally
distributed as long as the sample size is more than 30.
sampling distribution of variance with different sizes and fixed
repitions.
sizes <- c(2,20,25,30,35,40,45,50)
smplngDstrbtnSzChngVariance <- tibble()
for(i in sizes){
for(y in 1:1500){
nsample <- sample_n(train,size=i,replace=T) %>%
select(Age)
newRow <- nrow(smplngDstrbtnSzChngVariance) + 1
smplngDstrbtnSzChngVariance[newRow,"sizes"] <- i
smplngDstrbtnSzChngVariance[newRow,"variance"] <- sd(nsample$Age,na.rm = T)**2
}
}
gSamplingSizeVariance <- smplngDstrbtnSzChngVariance %>%
plot_ly(
x = ~variance,
frame = ~sizes,
type = "histogram"
)
gSamplingSizeVariance
Male Age - Female Age
age_male <- train %>%
select(Age,Sex) %>%
mutate(Sex = as_factor(Sex)) %>%
filter(Sex == "male")
age_female <- train %>%
select(Age,Sex) %>%
mutate(Sex = as_factor(Sex)) %>%
filter(Sex == "female")
sample_age_male_50 <- age_male %>%
rep_sample_n(size = 50,reps = 15000,replace = T) %>%
summarise(age_male_bar = mean(Age,na.rm = T))
sample_age_female_50 <- age_female %>%
rep_sample_n(size = 50,reps = 15000,replace = T) %>%
summarise(age_female_bar = mean(Age,na.rm = T))
samplediff_means <- sample_age_male_50$age_male_bar - sample_age_female_50$age_female_bar %>%
as_tibble()
gsamplediff_means <- samplediff_means %>%
ggplot(aes(value, y = ..density..)) +
geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
ggtitle("Distribution") +
theme_ipsum_rc()
gsamplediff_means

Survived Male - Survived Female
survived_male <- train %>%
select(Survived,Sex) %>%
filter(Sex == "male")
survived_female <- train %>%
select(Survived,Sex) %>%
filter(Sex == "female")
sample_survive_male_50 <- survived_male %>%
rep_sample_n(size = 50,reps = 15000,replace = T)
sample_survive_female_50 <- survived_female %>%
rep_sample_n(size = 50,reps = 15000,replace = T)
samplediff_survived <- sample_survive_male_50$Survived - sample_survive_female_50$Survived %>%
as_tibble()
gsamplediff_survived <- samplediff_survived %>%
ggplot(aes(value)) +
geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) +
ggtitle("Distribution") +
theme_ipsum_rc()
gsamplediff_survived

The plot is a bit hard to understand so it needs a bit of
explanation. \(-1\) represents the
female survived, \(0\) represents the
neither of them survived, and \(1\)
means that the male survived
After inspecting the plot, it becomes Crystal clear that there was a
bias in the rescue process where rescuers preferred to save female more
than males.
Confidence Intervals
n_size = 10
nsample <- sample_n(train,size= n_size,replace=T)
# Calculate The Mean
mean_est1=mean(nsample$Age,na.rm = T)
# Calculate The Standard Deviation
sd_est1=sd(nsample$Age,na.rm = T)
# Computing The Error Using the qnorm() Function to Calculate The Normal Distribution
error=qnorm(0.975)*(sd_est1/sqrt(n_size))
#Determining The Mean Interval[]
left <- mean_est1-error
right <- mean_est1+error
print(paste0("[",left,",",right,"]"))
[1] "[22.7480544835325,47.1519455164675]"
n_size = 50
nsample <- sample_n(train,size=n_size,replace=T)
# Calculate The Mean
mean_est2=mean(nsample$Age,na.rm = T)
# Calculate The Standard Deviation
sd_est2=sd(nsample$Age,na.rm = T)
# Computing The Error Using the qnorm() Function to Calculate The Normal Distribution
error=qnorm(0.975)*(sd_est1/sqrt(n_size))
#Determining The Mean Interval[]
left <- mean_est2-error
right <- mean_est2+error
print(paste0("[",left,",",right,"]"))
[1] "[23.083124073486,33.996875926514]"
Multiplication and addition of constant to the sample values
Multiplication
n_size = 200
nsample <- sample_n(train,size = n_size,replace = T)
nsample %>%
summarise(Age_mean = mean(Age), Age_sd = sd(Age),Age_var = sd(Age)**2)
nsample$Age <- nsample$Age *5
nsample %>%
summarise(Age_mean = mean(Age), Age_sd = sd(Age),Age_var = sd(Age)**2)
NA
Addition
n_size = 200
nsample <- sample_n(train,size = n_size,replace = T)
nsample %>%
summarise(Age_mean = mean(Age), Age_sd = sd(Age),Age_var = sd(Age)**2)
nsample$Age <- nsample$Age + 5
nsample %>%
summarise(Age_mean = mean(Age), Age_sd = sd(Age),Age_var = sd(Age)**2)
kernel distribution
A kernel distribution is a nonparametric representation of the
probability density function (\(pdf\))
of a random variable in any population
The kernel smoothing function defines the shape of the curve used to
generate the pdf Kernel distribution is Quote from histogram in other
word (smooth representation of a histogram) That the integral =1 There
is a benefit of smooth representation of a histogram like Ignores
irregularities and outliers , more efficient in approximation so it
deals better with large data than small data
\[ \hat{f_h} = \frac{1}{n} = \sum^n_{i =
1} K(x-x_i) = \frac{1}{nh} K\left(\frac{x-x_i}{h}\right) \]
Rules
Non-weighted Data
\[ \hat{f_h} = \frac{1}{n} \sum^n_{i = 1}
K(x-x_i) = \frac{1}{nh} K\left(\frac{x-x_i}{h}\right) \]
Weighted Data
\[
\hat{f_h} = \frac{1}{h} \sum^N_{i=1} w_i K \left(\frac{x-x_i}{h}\right),
\qquad \text{where} \sum^N_{i= 1} w_i = 1
\]
Kernel Function
- Box
- Triangle
- Normal
- Pantechnicon
Each density curve uses the same input data, but applies a different
kernel smoothing function to generate the pdf. The density estimates are
roughly comparable, but the shape of each curve varies slightly. For
example, the box kernel produces a density curve that is less smooth
than the others.
The choice of bandwidth value controls the smoothness of the
resulting probability density curve (higher value of \(h\) more smoothing)
Specifying a smaller bandwidth produces a very rough curve, but
reveals that there might be two major peaks in the data. Specifying a
larger bandwidth produces a curve nearly identical to the kernel
function Choosing the optimal (\(h\))
bandwidth methods :
- Silverman’s rule of thump that computes an optimal h by assuming
that data is normally distributed
- Improved Sheather Jones (ISJ) an algorithm is more robust with
multimodality data or a lot of data (one disadvantage is it needs to
large data )
Bounded domains data: have a constrains like data couldn’t be
negative ( -ve lead to probability = 0)
Mirror method
- Mirror the data
- Sum the original and mirrored kernel density estimate
- Chop it so that zero at the boundary side
2D
\(h\) : could be matrix (different
\(h\) in different directions)
The choice of norm comes into \(d \ge
2\)
The p-norm is \(||x||_p := (\sum_{i=1}
|x|^p)^{\frac{1}{p}}\) - norm-p =1 Manhattan distance - norm-p =2
Euclidean norm - norm-p =inf maximum norm (it’s not obvious in every
case which norm is the correct one)
standard euclidean distance is good choice because it invariant under
rotation as large data choice of k and p isn’t important so
Kolmogorov-Smirnov Test
Non parametric test
Kolmogorov-Smirnov Test is used to: 1. Decide if a sample comes from
a population with an expected continuous distribution (mostly normal
distribution) 2. To test for the difference in the shape of two sample
distributions

Compare overall shape of distribution, not specifically parameter
Kolmogorov-Smirnov Test is defined by a hypothesis: \(H_0\) : the data follow specific
distribution \(F(x) = F_T (x)\) \(H_a\) : the data don’t follow specific
distribution \(F(x) \neq F_T (x)\)
KS-test is made between some theoretical cumulative distribution
function (\(F_T(x)\)), and a sample
cumulative distribution function (\(F_s(x)\)) that measured by the statistic D,
which is the greatest vertical distance between them. \[ D = \underbrace{\text{sup}}_{x} | F_s(x) - F_t
(x)|\] 
- Determine \(x\) values
- Detemrine frequency of each observation
- Calculate cumulative frequency
- Calculate \(F_s(x) \rightarrow
\frac{\text{cumulative frequence}}{N}\) when \(n > 30\) (\(z\)-scores), when \(n<30\) (\(t\)-scores)
- calculate \(F_t(x)\)
- Calculate \(D \rightarrow F_s(x) -
F_T(x)\)
- Choose maximum \(D\)
- Calculate \(P\)-value
- Determine Kolmogorov’s quartile
- Compare \(p\)-value with
Kolmogorove’s quartile
LS0tDQp0aXRsZTogIlRpdGFuaWMgRGF0YSBBbmFseXNpcyINCmF1dGhvcjoNCi0gQWhtZWQgQXNocmFmDQotIE9tYXIgR2FtYWwgQWJkZWxheml6DQotIEFobWVkIFlvdXNyaQ0KLSBBaG1lZCBEYXdvb2QNCi0gQWxpYSBNZWRoYXQNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGluY2x1ZGVzOg0KICAgICAgaW5faGVhZGVyOiBwcmVhbWJsZS50ZXgNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCmFsd2F5c19hbGxvd19odG1sOiB5ZXMNCmVkaXRvcl9vcHRpb25zOg0KICBtYXJrZG93bjoNCiAgICB3cmFwOiA4MA0KLS0tDQoNCiMgSW50cm9kdWN0aW9uDQoNCkluIHRoaXMgcHJvamVjdCB3ZSBhcmUgdHJ5aW5nIGZpcnN0IHRvIGV4cGxvcmUgb3VyIGRhdGEgdG8gZ2V0IGEgYmV0dGVyDQp1bmRlcnN0YW5kaW5nIFRvIGFuc3dlciBvdXIgcXVlc3Rpb25zIHdlIGZpcnN0IG5lZWQgdG8gaGF2ZSBhDQpwcmVsaW1pbmFyeSBsb29rIGF0IG91ciBkYXRhLCBzbyB0aGF0IHdlIGNhbiBnZXQgYSBiZXR0ZXIgYSBpZGVhIHdoYXQgd2UNCmFyZSBkZWFsaW5nIHdpdGgsIGFzIHdlbGwgYXMgdGhlIHBvc3NpYmxlIG1pc3NpbmcgZGF0YSBhbmQgcmVsYXRpb25zaGlwcw0KdGhhdCBleGlzdA0KDQojIyBQcmVsaW1pbmFyeSBMb29rIGF0IHRoZSBkYXRhDQoNCldlIG5lZWQgZmlyc3QgdG8gZGVmaW5lIHRoZSBkYXRhIHdlIGhhdmUuDQoNCnwgVmFyaWFibGUgfCBEZWZpbml0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBLZXkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IHN1cnZpdmFsIHwgU3Vydml2YWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMCA9IE5vLCAxID0geWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBwY2xhc3MgICB8IHRpY2tldCBjbGFzcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDEgPSAxc3QsIDIgPSAybmQsIDMgPSAzcmQgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgc2V4ICAgICAgfCBzZXggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFnZSAgICAgIHwgQWdlIGluIHllYXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBzaWJzcCAgICB8IE51bWJlciBvZiBzaWJsaW5ncy9zcG91c2VzIGFib2FyZCB0aGUgdGl0YW5pYyB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgcGFyY2ggICAgfCBOdW1iZXIgb2YgcGFyZW50cy9jaGlsZHJlbiBhYm9hcmQgdGhlIFRpdGFuaWMgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHRpY2tldCAgIHwgdGlja2V0IG51bWJlcih1bmlxdWUpICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBmYXJlICAgICB8IFBhc3NlbmdlciBmYXJlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgY2FiaW4gICAgfCBDYWJpbiBudW1iZXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGVtYmFya2VkIHwgcG9ydCBvZiBlbWJhcmthdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQyA9IENoZXJib3VyZywgUSA9IFF1ZWVucy10b3duLCBTID0gU291dGhhbXB0b24gfA0KDQojIyBMb2FkaW5nIHRoZSBwYWNrYWdlcyBhbmQgdGhlIERhdGENCg0KYGBge3IgTG9hZGluZyBQYWNrYWdlcywgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiMgTG9hZGluZyBQYWNrYWdlcw0KIyMgdGlkeXZlcnNlIGxvYWRzIGRwbHlyIGFuZCByZWFkcg0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMjIFRvIGhhdmUgZGlmZmVyZW50IGNvbG9yIG1hcHMNCmxpYnJhcnkodmlyaWRpcykNCg0KIyMgZ2dwbG90MiB0byBwcm9kdWNlIGRpZmZlcmVudCBwbG90cw0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIyB1c2VzIGdncGxvdDIgdG8gcHJvZHVjZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCAtLSB0aGUgZGF0YSBtdXN0IGJlIGluIHRoZSBjb3JyZWN0IGZvcm0NCmxpYnJhcnkoZ2djb3JycGxvdCkNCg0KIyMgR2l2ZXMgdXMgYmV0dGVyIHRoZW1lcw0KbGlicmFyeShocmJydGhlbWVzKQ0KDQojIyB0byB1c2Ugc2tld25lc3MgZnVuLiB0byBjYWxjdWxhdGUgc2tld25lc3Mgb2YgdGhlIGRpc3RyaWJ1dGlvbg0KbGlicmFyeShlMTA3MSkNCg0KIyMgTXVsdGl2YXJpYXRlIGltcHV0YXRpb24gdXNpbmcgY2hhaW5lZCBlcXVhdGlvbnMgLS0gdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiBvdXIgZGF0YQ0KbGlicmFyeShtaWNlKQ0KDQojIyBMb2FkcyBkaWZmZXJlbnQgc3RhdGlzdGljYWwgZnVuY3Rpb25zDQpsaWJyYXJ5KHN0YXRzcikNCg0KIyMgVG8gcHJvZHVjZSBpbnRlcmFjdGl2ZSBwbG90DQpsaWJyYXJ5KHBsb3RseSkNCg0KDQojIExvYWRpbmcgVHJhaW5pbmcgRGF0YQ0KdHJhaW4gPC0gcmVhZF9jc3YoImRhdGEvdHJhaW4uY3N2IikNCg0KIyBMb2FkaW5nIFRlc3RpbmcgRGF0YQ0KdGVzdCA8LSByZWFkX2NzdigiZGF0YS90ZXN0LmNzdiIpDQoNCiMgQmluZGluZyB0aGVtIGludG8gYSBmdWxsIGRhdGEgZnJhbWUNCmRmIDwtIGJpbmRfcm93cyh0cmFpbix0ZXN0KQ0KYGBgDQoNCiMgRXhwbG9yYXRpb24gT2YgVGhlIERhdGENCg0KIyMgRGVzY3JpcHRpb24gb2YgdGhlIERhdGENCg0KIyMgU3VtbWFyeSBvZiBEYXRhDQoNCmBgYHtyIERhdGEgU3VtbWFyeX0NCnN1bW1hcnkodHJhaW4pDQpgYGANCg0KIyMgQ2F0ZWdvcmllcyBvZiBGZWF0dXJlcw0KDQo+IFF1YW50aXRhdGl2ZSBkYXRhIGFyZSBtZWFzdXJlcyBvZiB2YWx1ZXMgb3IgY291bnRzIGFuZCBhcmUgZXhwcmVzc2VkDQo+IGFzIG51bWJlcnMuDQoNCj4gUXVhbGl0YXRpdmUgZGF0YSBhcmUgbWVhc3VyZXMgb2YgJ3R5cGVzJyBhbmQgbWF5IGJlIHJlcHJlc2VudGVkIGJ5IGENCj4gbmFtZSwgc3ltYm9sLCBvciBhIG51bWJlciBjb2RlLg0KDQojIyMgUXVhbGl0aXZlDQoNCkNhdGVnb3JpY2FsOiBTdXJ2aXZlZCwgU2V4LCBhbmQgRW1iYXJrZWQuIE9yZGluYWw6IFBjbGFzcy4gTm9taW5hbDoNCk5hbWUuDQoNCiMjIyBRdWFudGl0aXZlDQoNCkNvbnRpbnVvdXM6IEFnZSwgRmFyZS4gRGlzY3JldGU6IFNpYlNwLCBQYXJjaC4NCg0KIyMjIE1peCB0eXBlcw0KDQpUaWNrZXQgaXMgYSBtaXggb2YgbnVtZXJpYyBhbmQgYWxwaGFudW1lcmljIGRhdGEgdHlwZXMgQ2FiaW4gaXMgbWl4DQpiZXR3ZWVuIGFscGhhIGFuZCBudW1lcmljDQoNCiMjIEV4cGxvcmluZyBNaXNzaW5nIERhdGEuDQoNCkNoZWNraW5nIGZvciBNaXNzaW5nIHZhbHVlcyBpbiBlYWNoIGZlYXR1cmUNCg0KYGBge3IgTWlzc2luZyBWYWx1ZXN9DQpjb2xTdW1zKGlzLm5hKHRyYWluKSkNCmBgYA0KDQpgYGB7ciBNaXNzaW5nIHZhbHVlcyBwbG90fQ0KbWlzc2luZ192YWx1ZXMgPC0gdHJhaW4gJT4lIHN1bW1hcml6ZV9hbGwoZnVucyhzdW0oaXMubmEoLikpL24oKSkpDQoNCm1pc3NpbmdfdmFsdWVzIDwtIGdhdGhlcihtaXNzaW5nX3ZhbHVlcywga2V5PSJmZWF0dXJlIiwgdmFsdWU9Im1pc3NpbmdfcGN0IikNCg0KbWlzc2luZ192YWx1ZXMNCg0KbWlzc2luZ192YWx1ZXMgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihmZWF0dXJlLC1taXNzaW5nX3BjdCkseT1taXNzaW5nX3BjdCkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLGZpbGw9InJlZCIpICsNCiAgY29vcmRfZmxpcCgpICsgIyB0byBmbGlwIHRoZSBncmFwaA0KICB4bGFiKCJGZWF0dXJlcyIpICsNCiAgeWxhYigiUGVyY2VudGFnZSBvZiBtaXNzaW5nIHZhbHVlcyIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9pcHN1bSgpDQpgYGANCg0KIyMjIE1pc3NpbmcgZGF0YSwgaXQgaXMgbm9ybWFsPw0KDQpJdCBpcyBxdWl0ZSBub3JtYWwgdG8gc2VlIG1pc3NpbmcgZGF0YSBpbiBhbnkgZGF0YS1zZXQgYXMgc3VjaCBkYXRhIGlzDQpjb2xsZWN0ZWQgYnkgbWFudWFsbHkgd2hpY2ggbWVhbnMgdGhhdCB0aGVyZSBtaWdodCBiZSBzb21lIGVycm9yLg0KTWlzc2luZyBkYXRhIHByZXNlbnQgdmFyaW91cyBwcm9ibGVtcy4gRmlyc3QsIHRoZSBhYnNlbmNlIG9mIGRhdGENCnJlZHVjZXMgc3RhdGlzdGljYWwgcG93ZXIsIHdoaWNoIHJlZmVycyB0byB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgdGVzdA0Kd2lsbCByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyB3aGVuIGl0IGlzIGZhbHNlLiBTZWNvbmQsIHRoZSBsb3N0IGRhdGENCmNhbiBjYXVzZSBiaWFzIGluIHRoZSBlc3RpbWF0aW9uIG9mIHBhcmFtZXRlcnMuIFRoaXJkLCBpdCBjYW4gcmVkdWNlIHRoZQ0KcmVwcmVzZW50YXRpdmVuZXNzIG9mIHRoZSBzYW1wbGVzLg0KDQojIyMgVGhlIHNvbHV0aW9uIHRvIHRoZSBtaXNzaW5nIGRhdGENCg0KIyMjIyBSZW1vdmFsDQoNClRoZSByZW1vdmFsIG9mIHRoZSBvYnNlcnZhdGlvbnMgdGhhdCBjb250YWluIG1pc3NpbmcgbWlnaHQgY2F1c2UgYQ0KYmlnZ2VyIGlzc3VlIHdoZXJlIGl0IG1pZ2h0IGFuIGV2ZW4gYmlnZ2VyIGxvc3Mgb2YgaW5mb3JtYXRpb24gd2hpY2gNCmNhdXNlIGJpYXMgaW4gb3VyIGVzdGltYXRpb24uIEJ1dCBvbiBjbG9zZSBvYnNlcnZhdGlvbiBvZiBvdXIgZGF0YSB3ZQ0KY2FuIGlkZW50aWZ5IHRoYXQgdGhlcmUgYXJlIHNvbWUgZmVhdHVyZXMgdGhhdCBhcmUgbm90IGV4dHJlbWVseSB1c2VmdWwNCmFuZCBjb250YWluIG1pc3NpbmcgZGF0YSwgc28gd2UgY2FuIGRyb3Agc3VjaCBmZWF0dXJlcy4NCg0KYGBge3IgZHJvcGluZyBjYWJpbn0NCnRyYWluIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoIUNhYmluKQ0Kc3VtbWFyeSh0cmFpbikNCmBgYA0KDQojIyMjIEltcHV0YXRpb24NCg0KSW1wdXRpbmcgdGhlIG1pc3NpbmcgZGF0YSBnaXZlcyB0aGUgYWR2YW50YWdlIHRoYXQgd2UgdXNlIGEgbGVhcm5pbmcNCm1vZGVsIHRvIHByZWRpY3Qgc3VjaCBtaXNzaW5nIGRhdGEgYW5kIG1haW50YWluIHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyDQpkYXRhLg0KDQojIyBIaXN0b2dyYW0gb2YgQWdlIGZlYXR1cmUuDQoNCmBgYHtyIEFnZSBoaXN0b2dyYW19DQpnQWdlRGVuc2l0eSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KEFnZSkgJT4lDQogIGdncGxvdChhZXMoQWdlLCB5ID0gLi5kZW5zaXR5Li4pKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMCxiaW53aWR0aCA9IDEsY29sb3I9aW5mZXJubygxLGFscGhhPTEpKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbD1pbmZlcm5vKDEsYmVnaW4gPSAwLjUsYWxwaGEgPSAwLjUpLGNvbG9yID0gaW5mZXJubygxLGJlZ2luPTApKSArIA0KICBhbm5vdGF0ZSgNCiAgICAidGV4dCIsDQogICAgeCA9IDcwLA0KICAgIHkgPSAwLjA0LA0KICAgIGxhYmVsID0gcGFzdGUoIlNrZXduZXNzOiIsc2tld25lc3ModHJhaW4kQWdlLG5hLnJtID0gVCkpLA0KICAgIGNvbG91ciA9IGluZmVybm8oMSxiZWdpbiA9IDAuMSksDQogICAgc2l6ZSA9IDQNCiAgKSArIA0KICB0aGVtZV9pcHN1bV9yYygpDQoNCmdBZ2VEZW5zaXR5DQpgYGANCg0KIyMjIFBvcHVsYXRpb24gbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIEFnZSBmZWF0dXJlIEJlZm9yZSBpbXB1dGF0aW9uDQoNCmBgYHtyIFN1bW1hcnkgb2YgQWdlIGZlYXR1cmV9DQp0cmFpbiAlPiUNCiAgc3VtbWFyaXNlKEFnZV9tZWFuID0gbWVhbihBZ2UsbmEucm0gPSBUKSwgQWdlX3NkID0gc2QoQWdlLG5hLnJtID0gVCkpDQpgYGANCg0KIyMgSGlzdG9ncmFtIG9mIEZhcmUgZmVhdHVyZQ0KDQpgYGB7ciBIaXN0b2dyYW0gb2YgRmFyZSBGZWF0dXJlfQ0KZ0ZhcmVEZW5zaXR5IDwtIHRyYWluICU+JQ0KICBzZWxlY3QoRmFyZSkgJT4lDQogIGdncGxvdChhZXMoRmFyZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAsYmlud2lkdGggPSAxLGNvbG9yPXZpcmlkaXMoMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IHZpcmlkaXMoMSxiZWdpbj0wKSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwwLjA1KSkrDQogIHRoZW1lX2lwc3VtX3JjKCkgKyANCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSAyMDAsDQogICAgeSA9IDAuMDUsDQogICAgbGFiZWwgPSBwYXN0ZSgiU2tld25lc3MiLHNrZXduZXNzKHRyYWluJEZhcmUpKSwNCiAgICBjb2xvdXIgPSAiYmxhY2siLA0KICAgIHNpemUgPSA0DQogICkNCg0KZ0ZhcmVEZW5zaXR5IA0KYGBgDQoNCiMjIyBQb3B1bGF0aW9uIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhaXRvbiBvZiBGYXJlIGZlYXR1cmUuDQoNCmBgYHtyIFN1bW1hcnkgb2YgRmFyZSBGZWF0dXJlfQ0KdHJhaW4gJT4lDQogIHN1bW1hcmlzZShGYXJlX21lYW4gPSBtZWFuKEZhcmUsbmEucm0gPSBUKSwgRmFyZV9zZCA9IHNkKEZhcmUsbmEucm0gPSBUKSkNCmBgYA0KDQojIyBJbXB1dGluZyB0aGUgbWlzc2luZyBhZ2UgZmVhdHVyZQ0KDQpgYGB7ciBJbXB1dGF0aW9ufQ0KIy0tLS0tLS0tLS0tLS0tLU1JQ0UtLS0tLS0tLS0tLS0tLQ0Kc2V0LnNlZWQoMTI5KQ0KbWljZV9tb2QgPC0gbWljZSh0cmFpblssIW5hbWVzKHRyYWluKSAlaW4lIGMoJ1Bhc3NlbmdlcklkJywnTmFtZScsJ1RpY2tldCcsJ0NhYmluJywnU3Vydml2ZWQnKV0sbWV0aG9kID0gJ3JmJykNCm1pY2Vfb3V0cHV0IDwtIGNvbXBsZXRlKG1pY2VfbW9kKQ0KDQpnZGlzdHJPcmlnaW5hbERhdGEgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKw0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2Ygb3JpZ2luYWwgZGF0YSIpICsNCiAgdGhlbWVfaXBzdW1fcmMoKQ0KZ2Rpc3RyTUlDRURhdGEgPC0gbWljZV9vdXRwdXQgJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIG1pY2Ugb3V0cHV0IikgKw0KICB0aGVtZV9pcHN1bV9yYygpDQoNCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdkaXN0ck9yaWdpbmFsRGF0YSxnZGlzdHJNSUNFRGF0YSxucm93ID0gMSkNCg0KdHJhaW4kQWdlIDwtIG1pY2Vfb3V0cHV0JEFnZQ0KDQptaXNzaW5nX3ZhbHVlcyA8LSB0cmFpbiAlPiUgc3VtbWFyaXplX2FsbChmdW5zKHN1bShpcy5uYSguKSkvbigpKSkNCg0KbWlzc2luZ192YWx1ZXMgPC0gZ2F0aGVyKG1pc3NpbmdfdmFsdWVzLCBrZXk9ImZlYXR1cmUiLCB2YWx1ZT0ibWlzc2luZ19wY3QiKQ0KDQptaXNzaW5nX3ZhbHVlcw0KDQptaXNzaW5nX3ZhbHVlcyAlPiUgDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKGZlYXR1cmUsLW1pc3NpbmdfcGN0KSx5PW1pc3NpbmdfcGN0KSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsZmlsbD0icmVkIikgKw0KICBjb29yZF9mbGlwKCkgKyAjIHRvIGZsaXAgdGhlIGdyYXBoDQogIHhsYWIoIkZlYXR1cmVzIikgKw0KICB5bGFiKCJQZXJjZW50YWdlIG9mIG1pc3NpbmcgdmFsdWVzIikrDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArDQogIHRoZW1lX2lwc3VtKCkNCmBgYA0KDQojIyMgUG9wdWxhdGlvbiBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24NCg0KYGBge3IgU3VtbWFyeSBvZiBBZ2UgZmVhdHVyZSBBZnRlciBJbXB1dGF0aW9ufQ0KdHJhaW4gJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlLG5hLnJtID0gVCksIEFnZV9zZCA9IHNkKEFnZSxuYS5ybSA9IFQpKQ0KYGBgDQoNCiMjIERldGVybWluaW5nIHRoZSBkaXN0cnVidXR1aW9uIG9mIEFnZSBhbmQgRmFyZSBCeSBpbnNwZWN0aW9uDQoNClRob3VnaCBkZXRlcm1pbmF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gdXNpbmcgaW5zcGVjdGlvbiBpcyBsaWtlbHkgbm90DQpnb2luZyB0byBiZSBlZmZlY3RpdmUgd2UgYXJlIGdvaW5nIHRvIHRoZSBLUy10ZXN0IGluIGEgbGF0ZXIgc2VjdGlvbg0KDQojIyMgQWdlDQoNClRoZSBoaXN0b2dyYW0gb2YgdGhlIEFnZSBmZWF0dXJlIGxvb2sgdmVyeSBtdWNoIGxpa2UgYSBub3JtYWwNCmRpc3RyaWJ1dGlvbiwgeWV0IGl0J3Mgbm90IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpdHNlbGYuDQoNCiMjIyBGYXJlDQoNClRoZSBoaXN0b2dyYW0gb2YgdGhlIEZhcmUgZmVhdHVyZSBmaXRzIHRoZSAkXGNoaV4yJCBkaXN0cmlidXRpb24uDQoNCiMjIFBsb3R0aW5nIFRoZSBEYXRhDQoNCldlIGluY2x1ZGUgZXZlbiBtb3JlIHBsb3RzIG9mIG91ciBkYXRhIHRvIGdldCBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mDQppdCwgaGVscCB1cyBzZWUgaGlkZGVuIGNvcnJlbGF0aW9ucyBhbmQgd2F5cyB0byBmYWNpbGl0YXRlIG91ciBhbmFseXNpcw0KDQojIyMgQ29ycmVsYXRpb24gTWF0cml4DQoNCldlIGFyZSBnb2luZyB0byB1c2UgY29ycmVsYXRpb24gbWF0cml4IG9mIHRoZSBudW1lcmljYWwgZGF0YSB0byBhc3Nlc3MNCnRoZSBjb3JyZWxhdGlvbiwgd2hpY2ggbWlnaHQgZ2l2ZXMgYSBiZXR0ZXIgaWRlYSBvZiB3aGljaCBmZWF0dXJlIG1pZ2h0DQpiZSBpbXBvcnRhbnQNCg0KYGBge3IgQ29ycmVsYXRpb24gTWF0cml4LCBmaWcuYWxpZ249J2NlbnRlcid9DQpjb3JyZWxhdGlvbk1hdHJpeCA8LSB0cmFpbiAlPiUNCiAgZmlsdGVyKCFpcy5uYShBZ2UpKSAlPiUNCiAgc2VsZWN0KFN1cnZpdmVkLCBQY2xhc3MsQWdlLFNpYlNwLFBhcmNoLEZhcmUpICU+JQ0KICBjb3IoKSAlPiUNCiAgZ2djb3JycGxvdChsYWIgPSBULA0KICAgICAgICAgICAgIGdndGhlbWUgPXRoZW1lX2lwc3VtX3JjKGdyaWQgPSBGKSwNCiAgICAgICAgICAgICB0aXRsZT0iQ29ycmVsYXRpb24gTWF0cml4IixoYy5vcmRlcj1ULA0KICAgICAgICAgICAgIGNvbG9ycyA9cmV2KHZpcmlkaXMoMyxhbHBoYT0wLjcpKSwNCiAgICAgICAgICAgICBkaWdpdHMgPSAxKQ0KDQpjb3JyZWxhdGlvbk1hdHJpeA0KYGBgDQoNClRoZSBmYXJlIGZlYXR1cmVzIHNlZW1zIHRvIGJlIHRoZSBtb3N0IGNvcnJlbGF0ZWQgZmVhdHVyZSB0byBzdXJ2aXZhbCBvZg0KdGhlIHBhc3NlbmdlcnMsIGJ1dCBpdCBkb2Vzbid0IG5lZ2F0ZSB0aGUgaW1wb3J0YW5jZSBvZiB0aGUgb3RoZXINCmZlYXR1cmVzIGluIHRoZSBkYXRhLiBXaGljaCBtZWFucyB0aGF0IHdlIHdpbGwgc3RhcnQgYnkgY29tcGFyaW5nIHRoZQ0KZWFjaCB0aGF0IHdlIGNvbnNpZGVyIHRvIGJlIGltcG9ydGFudCBhZ2FpbnN0IHN1cnZpdmFsIGZlYXR1cmUNCg0KIyMjIENsYXNzIG9mIFBhc3NlbmdlciBWcyBTdXJ2aXZlZA0KDQpgYGB7ciBQY2xhc3NWU1N1cnZpdmVkLGZpZy5hbGlnbj0nY2VudGVyJ30NCmdQY2xhc3NTdXJ2aXZlZCA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KFBjbGFzcyxTdXJ2aXZlZCkgJT4lDQogIGdncGxvdChhZXMoYXNfZmFjdG9yKFBjbGFzcyksZmlsbD1hc19mYWN0b3IoU3Vydml2ZWQpKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsNCiAgdGhlbWVfaXBzdW1fcmMoKSArIA0KICBsYWJzKHggPSAiQ2xhc3NlcyIseSA9ICJTdXJ2aXZhbCBSYXRlIikrDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJTdXJ2aXZlZCIsIGxhYmVscyA9IGMoIkRpZG4ndCBTdXJ2aXZlIiwiU3Vydml2ZWQiKSkNCg0KZ1BjbGFzc1N1cnZpdmVkDQpgYGANCg0KRnJvbSB0aGlzIFBsb3QsIGl0IHNlZW1zIGNsZWFyIHRoYXQgcGVvcGxlIGZyb20gdGhlIHVwcGVyIGNsYXNzZXMgaGFkIGhpZ2hlciBzdXJ2aXZhbCByYXRlcywgdGhvdWdodCB0aGlzIHNlZW1lZCBvYnZpb3VzIGZyb20gdGhlIGJlZ2lubmluZy4NCiMjIyBTaWJsaW5ncyBhbmQgU3BvdXNlcyBWcyBTdXJ2aXZlZA0KDQpgYGB7ciBTaWJTcFZTU3Vydml2ZWQsZmlnLmFsaWduPSdjZW50ZXInfQ0KZ1NpYlNwU3Vydml2ZWQgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChTaWJTcCxTdXJ2aXZlZCkgJT4lDQogIGdncGxvdChhZXMoYXNfZmFjdG9yKFNpYlNwKSxmaWxsPWFzX2ZhY3RvcihTdXJ2aXZlZCkpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIGxhYnMoeCA9ICJTaWJsaW5ncyBhbmQgU3BvdXNlcyIseSA9ICJTdXJ2aXZhbCBSYXRlIikrDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJTdXJ2aXZlZCIsIGxhYmVscyA9IGMoIkRpZG4ndCBTdXJ2aXZlIiwiU3Vydml2ZWQiKSkgKw0KICB0aGVtZV9pcHN1bSgpDQoNCmdTaWJTcFN1cnZpdmVkDQpgYGANCg0KVGhpcyBwbG90IGhpZ2hsaWdodCBhIHBvaW50IHRoYXQgbWlnaHQgYmUgY291bnRlci1pbnR1aXRpdmUgdGhhdCBwZW9wbGUgdGhhdCBoYXZlIG5vIHNpYmxpbmdzIG9yIHNwb3VzZXMgaGFkIGxvd2VyIHN1cnZpdmFsIHJhdGVzIHRoYW4gdGhvc2Ugd2l0aCBhdCBsZWFzdCBvbmUgc2libGluZyBvciBhIHNwb3VzZQ0KDQojIyMgTnVtYmVyIG9mIGNoaWxkcmVuL3BhcmVudHMgVnMgU3Vydml2ZWQNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInfQ0KZ1BhcmNoU3Vydml2ZWQgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChQYXJjaCxTdXJ2aXZlZCkgJT4lDQogIGdncGxvdChhZXMoYXNfZmFjdG9yKFBhcmNoKSxmaWxsPWFzX2ZhY3RvcihTdXJ2aXZlZCkpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWwgPSBzY2FsZXM6OnBlcmNlbnQpKw0KICBsYWJzKHggPSAiTnVtYmVyIG9mIHBhcmVudHMvY2hpbGRyZW4iLHkgPSAiU3Vydml2YWwgUmF0ZSIpKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiU3Vydml2ZWQiLCBsYWJlbHMgPSBjKCJEaWRuJ3QgU3Vydml2ZSIsIlN1cnZpdmVkIikpICsNCiAgdGhlbWVfaXBzdW1fcmMoKQ0KZ1BhcmNoU3Vydml2ZWQNCmBgYA0KDQojIyMgR2VuZGVyIFZTIFN1cnZpdmVkDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdTZXhTdXJ2aXZlZCA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KFNleCxTdXJ2aXZlZCkgJT4lDQogIGdncGxvdChhZXMoYXNfZmFjdG9yKFNleCksZmlsbCA9IGFzX2ZhY3RvcihTdXJ2aXZlZCkpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWwgPSBzY2FsZXM6OnBlcmNlbnQpICsgDQogIGxhYnMoeCA9ICJTZXgiLHkgPSAiU3Vydml2YWwgUmF0ZSIpKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiU3Vydml2ZWQiLCBsYWJlbHMgPSBjKCJEaWRuJ3QgU3Vydml2ZSIsIlN1cnZpdmVkIikpICsNCiAgdGhlbWVfaXBzdW1fcmMoKQ0KcHJpbnQoZ1NleFN1cnZpdmVkKQ0KYGBgDQojIyMgU3Vydml2YWwgRGVuc2l0eSBhbmQgQWdlDQpgYGB7cn0NCmdTdXJ2aXZhbEFnZURlbnNpdHkgPC0gZ2dwbG90KHRyYWluWyghaXMubmEodHJhaW4kU3Vydml2ZWQpICYgIWlzLm5hKHRyYWluJEFnZSkpLF0sDQogICAgICAgICAgICBhZXMoeCA9IEFnZSwNCiAgICAgICAgICAgICAgICBmaWxsID0gU3Vydml2ZWQpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYT0wLjUsDQogICAgICAgICAgICAgICBhZXMoZmlsbD1hc19mYWN0b3IoU3Vydml2ZWQpKSkgKw0KICBsYWJzKHRpdGxlPSJTdXJ2aXZhbCBkZW5zaXR5IGFuZCBBZ2UiKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDEwKSkgKw0KICB0aGVtZV9pcHN1bSgpDQoNCmdTdXJ2aXZhbEFnZURlbnNpdHkNCmBgYA0KDQojIyMgRGFzaGJvYXJkIG9mIHRoZSBwcmV2aW91cyBncmFwaHMNCg0KYGBge3J9DQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShnUGNsYXNzU3Vydml2ZWQsDQogICAgICAgICAgICAgICAgICAgICAgICBnU2liU3BTdXJ2aXZlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGdTZXhTdXJ2aXZlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGdQYXJjaFN1cnZpdmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgbnJvdz0yKQ0KYGBgDQoNCmBgYHtyfQ0KdHJhaW4gJT4lDQogIGdyb3VwX2J5KFNleCkgJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlLG5hLnJtPVRSVUUpLA0KICAgICAgICAgICAgYWdlX3NkID0gc2QoQWdlLG5hLnJtPVQpLA0KICAgICAgICAgICAgc3VyaXZhbF9tZWFuID0gbWVhbihTdXJ2aXZlZCxuYS5ybSA9VCksDQogICAgICAgICAgICBzdXJpdmFsX3NkID0gc2QoU3Vydml2ZWQsbmEucm0gPSBUKSkNCg0KYGBgDQoNCiMjIFNhbXBsaW5nDQoNCiMjIyBSYW5kb20gc2FtcGxlIG9mIHNpemUgb2YgNTAgKEFnZSkNCg0KYGBge3IgU2FtcGxpbmctMX0NCnNhbXBsZV81MCA8LSBzYW1wbGVfbih0cmFpbixzaXplID0gNTAscmVwbGFjZSA9IFQpICU+JQ0KICBzdW1tYXJpc2UobWVhbiA9IG1lYW4oQWdlKSwgc2QgPSBzZChBZ2UpKQ0Kc2FtcGxlXzUwDQpgYGANCiMjIyBTYW1wbGluZyBkaXN0cnVidXRpb24gd2l0aCBhIGZpeGVkIHNpemUgKDUwKSAobWVhbikNCmBgYHtyfQ0KbnVtT2ZTYW1wbGVzIDwtIHNlcSg1MCwxMDAwLDUwKQ0KDQpzbXBsbmdEc3RyYnRuUnBzQ2huZyA8LSB0aWJibGUoKQ0KDQpmb3IoaSBpbiBudW1PZlNhbXBsZXMpew0KICANCiAgZm9yKHkgaW4gMTppKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9NTAscmVwbGFjZT1UKSAlPiUNCiAgICAgIHNlbGVjdChBZ2UpDQogICAgbmV3Um93IDwtIG5yb3coc21wbG5nRHN0cmJ0blJwc0NobmcpICsgMQ0KICAgIHNtcGxuZ0RzdHJidG5ScHNDaG5nW25ld1JvdywicmVwcyJdIDwtIGkNCiAgICBzbXBsbmdEc3RyYnRuUnBzQ2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCiAgDQp9DQoNCmdTYW1wbGluZ1JlcHMgPC0gc21wbG5nRHN0cmJ0blJwc0NobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5yZXBzLA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIg0KICApDQpnU2FtcGxpbmdSZXBzDQpgYGANCldoaWxlIG5vLiBvZiBzYW1wbGUgc2l6ZSBpbmNyZWFzZSB0aGUgdmFyaWFiaWxpdHkgb2Ygc2FtcGxpbmcNCmRpc3RyaWJ1dGlvbiBkZWNyZWFzZSBhbmQgdGhlIG1lYW4gaW5jcmVhc2UuDQoNCiMjIyBTYW1wbGluZyBkaXN0cmlidXRpb24gd2l0aCBhIGZpeGVkIHJlcGV0aXRpb25zIGFuZCBkaWZmZXJlbnQgc2l6ZXMgKG1lYW4pDQoNCmBgYHtyIHNhbXBsaW5nLWRpc3RydWJ1dGlvbi1zaXplc30NCnNpemVzIDwtIHNlcSgyMCwyNjAsMjApDQpzbXBsbmdEc3RyYnRuU3pDaG5nIDwtIHRpYmJsZSgpDQpmb3IoaSBpbiBzaXplcyl7DQogIGZvcih5IGluIDE6MTUwMCl7DQogICAgbnNhbXBsZSA8LSBzYW1wbGVfbih0cmFpbixzaXplPWkscmVwbGFjZT1UKSAlPiUNCiAgICAgIHNlbGVjdChBZ2UpDQogICAgbmV3Um93IDwtIG5yb3coc21wbG5nRHN0cmJ0blN6Q2huZykgKyAxDQogICAgc21wbG5nRHN0cmJ0blN6Q2huZ1tuZXdSb3csInNpemVzIl0gPC0gaQ0KICAgIHNtcGxuZ0RzdHJidG5TekNobmdbbmV3Um93LCJ4X2JhciJdIDwtIG1lYW4obnNhbXBsZSRBZ2UsbmEucm0gPSBUKQ0KICB9DQp9DQpnU2FtcGxpbmdTaXplIDwtIHNtcGxuZ0RzdHJidG5TekNobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5zaXplcywNCiAgICB0eXBlID0gImhpc3RvZ3JhbSINCiAgKQ0KZ1NhbXBsaW5nU2l6ZQ0KYGBgDQoNCldoaWxlIG5vLiBvZiBzYW1wbGUgc2l6ZSBpbmNyZWFzZSB0aGUgdmFyaWFiaWxpdHkgb2Ygc2FtcGxpbmcNCmRpc3RyaWJ1dGlvbiBkZWNyZWFzZSxhbmQgVGhlIHNhbXBsZSBkaXN0cmlidXRpb24gbWVhbiB3aWxsIGJlIG5vcm1hbGx5DQpkaXN0cmlidXRlZCBhcyBsb25nIGFzIHRoZSBzYW1wbGUgc2l6ZSBpcyBtb3JlIHRoYW4gMzAuDQoNCiMjIyBzYW1wbGluZyBkaXN0cmlidXRpb24gb2YgdmFyaWFuY2Ugd2l0aCBkaWZmZXJlbnQgc2l6ZXMgYW5kIGZpeGVkIHJlcGl0aW9ucy4NCg0KYGBge3J9DQpzaXplcyA8LSBjKDIsMjAsMjUsMzAsMzUsNDAsNDUsNTApDQpzbXBsbmdEc3RyYnRuU3pDaG5nVmFyaWFuY2UgPC0gdGliYmxlKCkNCmZvcihpIGluIHNpemVzKXsNCiAgZm9yKHkgaW4gMToxNTAwKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9aSxyZXBsYWNlPVQpICU+JQ0KICAgICAgc2VsZWN0KEFnZSkNCiAgICBuZXdSb3cgPC0gbnJvdyhzbXBsbmdEc3RyYnRuU3pDaG5nVmFyaWFuY2UpICsgMQ0KICAgIHNtcGxuZ0RzdHJidG5TekNobmdWYXJpYW5jZVtuZXdSb3csInNpemVzIl0gPC0gaQ0KICAgIHNtcGxuZ0RzdHJidG5TekNobmdWYXJpYW5jZVtuZXdSb3csInZhcmlhbmNlIl0gPC0gc2QobnNhbXBsZSRBZ2UsbmEucm0gPSBUKSoqMg0KICB9DQp9DQpnU2FtcGxpbmdTaXplVmFyaWFuY2UgPC0gc21wbG5nRHN0cmJ0blN6Q2huZ1ZhcmlhbmNlICU+JQ0KICBwbG90X2x5KA0KICAgIHggPSB+dmFyaWFuY2UsDQogICAgZnJhbWUgPSB+c2l6ZXMsDQogICAgdHlwZSA9ICJoaXN0b2dyYW0iDQogICkNCmdTYW1wbGluZ1NpemVWYXJpYW5jZQ0KYGBgDQoNCiMjIE1hbGUgQWdlIC0gRmVtYWxlIEFnZQ0KYGBge3J9DQphZ2VfbWFsZSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KEFnZSxTZXgpICU+JQ0KICBtdXRhdGUoU2V4ID0gYXNfZmFjdG9yKFNleCkpICU+JQ0KICBmaWx0ZXIoU2V4ID09ICJtYWxlIikNCmFnZV9mZW1hbGUgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChBZ2UsU2V4KSAlPiUNCiAgbXV0YXRlKFNleCA9IGFzX2ZhY3RvcihTZXgpKSAlPiUNCiAgZmlsdGVyKFNleCA9PSAiZmVtYWxlIikNCg0Kc2FtcGxlX2FnZV9tYWxlXzUwIDwtIGFnZV9tYWxlICU+JQ0KICByZXBfc2FtcGxlX24oc2l6ZSA9IDUwLHJlcHMgPSAxNTAwMCxyZXBsYWNlID0gVCkgJT4lDQogIHN1bW1hcmlzZShhZ2VfbWFsZV9iYXIgPSBtZWFuKEFnZSxuYS5ybSA9IFQpKQ0Kc2FtcGxlX2FnZV9mZW1hbGVfNTAgPC0gYWdlX2ZlbWFsZSAlPiUNCiAgcmVwX3NhbXBsZV9uKHNpemUgPSA1MCxyZXBzID0gMTUwMDAscmVwbGFjZSA9IFQpICU+JQ0KICBzdW1tYXJpc2UoYWdlX2ZlbWFsZV9iYXIgPSBtZWFuKEFnZSxuYS5ybSA9IFQpKQ0KDQpzYW1wbGVkaWZmX21lYW5zIDwtIHNhbXBsZV9hZ2VfbWFsZV81MCRhZ2VfbWFsZV9iYXIgLSAgc2FtcGxlX2FnZV9mZW1hbGVfNTAkYWdlX2ZlbWFsZV9iYXIgJT4lDQogIGFzX3RpYmJsZSgpDQoNCmdzYW1wbGVkaWZmX21lYW5zIDwtIHNhbXBsZWRpZmZfbWVhbnMgJT4lDQogIGdncGxvdChhZXModmFsdWUsIHkgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDI1LGJpbndpZHRoID0gMSxjb2xvcj1pbmZlcm5vKDEsYWxwaGE9MSkpICsgDQogIGdlb21fZGVuc2l0eShmaWxsPWluZmVybm8oMSxiZWdpbiA9IDAuNSxhbHBoYSA9IDAuNSksY29sb3IgPSBpbmZlcm5vKDEsYmVnaW49MCkpICsNCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIikgKw0KICB0aGVtZV9pcHN1bV9yYygpDQpnc2FtcGxlZGlmZl9tZWFucw0KYGBgDQoNCiMjIFN1cnZpdmVkIE1hbGUgLSBTdXJ2aXZlZCBGZW1hbGUNCg0KYGBge3J9DQpzdXJ2aXZlZF9tYWxlIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoU3Vydml2ZWQsU2V4KSAlPiUNCiAgZmlsdGVyKFNleCA9PSAibWFsZSIpDQoNCnN1cnZpdmVkX2ZlbWFsZSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KFN1cnZpdmVkLFNleCkgJT4lDQogIGZpbHRlcihTZXggPT0gImZlbWFsZSIpDQoNCnNhbXBsZV9zdXJ2aXZlX21hbGVfNTAgPC0gc3Vydml2ZWRfbWFsZSAlPiUNCiAgcmVwX3NhbXBsZV9uKHNpemUgPSA1MCxyZXBzID0gMTUwMDAscmVwbGFjZSA9IFQpDQpzYW1wbGVfc3Vydml2ZV9mZW1hbGVfNTAgPC0gc3Vydml2ZWRfZmVtYWxlICU+JQ0KICByZXBfc2FtcGxlX24oc2l6ZSA9IDUwLHJlcHMgPSAxNTAwMCxyZXBsYWNlID0gVCkNCnNhbXBsZWRpZmZfc3Vydml2ZWQgPC0gc2FtcGxlX3N1cnZpdmVfbWFsZV81MCRTdXJ2aXZlZCAtIHNhbXBsZV9zdXJ2aXZlX2ZlbWFsZV81MCRTdXJ2aXZlZCAlPiUgDQogIGFzX3RpYmJsZSgpDQoNCmdzYW1wbGVkaWZmX3N1cnZpdmVkIDwtIHNhbXBsZWRpZmZfc3Vydml2ZWQgJT4lDQogIGdncGxvdChhZXModmFsdWUpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyNSxiaW53aWR0aCA9IDEsY29sb3I9aW5mZXJubygxLGFscGhhPTEpKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24iKSArDQogIHRoZW1lX2lwc3VtX3JjKCkNCmdzYW1wbGVkaWZmX3N1cnZpdmVkDQpgYGANCg0KVGhlIHBsb3QgaXMgYSBiaXQgaGFyZCB0byB1bmRlcnN0YW5kIHNvIGl0IG5lZWRzIGEgYml0IG9mIGV4cGxhbmF0aW9uLiAkLTEkIHJlcHJlc2VudHMgdGhlIGZlbWFsZSBzdXJ2aXZlZCwgJDAkIHJlcHJlc2VudHMgdGhlIG5laXRoZXIgb2YgdGhlbSBzdXJ2aXZlZCwgYW5kICQxJCBtZWFucyB0aGF0IHRoZSBtYWxlIHN1cnZpdmVkDQoNCkFmdGVyIGluc3BlY3RpbmcgdGhlIHBsb3QsIGl0IGJlY29tZXMgQ3J5c3RhbCBjbGVhciB0aGF0IHRoZXJlIHdhcyBhIGJpYXMgaW4gdGhlIHJlc2N1ZSBwcm9jZXNzIHdoZXJlIHJlc2N1ZXJzIHByZWZlcnJlZCB0byBzYXZlIGZlbWFsZSBtb3JlIHRoYW4gbWFsZXMuDQoNCiMjIENvbmZpZGVuY2UgSW50ZXJ2YWxzDQpgYGB7cn0NCm5fc2l6ZSA9IDEwDQpuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9IG5fc2l6ZSxyZXBsYWNlPVQpDQoNCiMgQ2FsY3VsYXRlIFRoZSBNZWFuDQptZWFuX2VzdDE9bWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQoNCiMgQ2FsY3VsYXRlIFRoZSBTdGFuZGFyZCBEZXZpYXRpb24NCnNkX2VzdDE9c2QobnNhbXBsZSRBZ2UsbmEucm0gPSBUKQ0KDQojIENvbXB1dGluZyBUaGUgRXJyb3IgVXNpbmcgdGhlIHFub3JtKCkgRnVuY3Rpb24gdG8gQ2FsY3VsYXRlIFRoZSBOb3JtYWwgRGlzdHJpYnV0aW9uIA0KZXJyb3I9cW5vcm0oMC45NzUpKihzZF9lc3QxL3NxcnQobl9zaXplKSkNCg0KI0RldGVybWluaW5nIFRoZSBNZWFuIEludGVydmFsW10NCmxlZnQgPC0gbWVhbl9lc3QxLWVycm9yDQpyaWdodCA8LSBtZWFuX2VzdDErZXJyb3INCg0KcHJpbnQocGFzdGUwKCJbIixsZWZ0LCIsIixyaWdodCwiXSIpKQ0KYGBgDQoNCmBgYHtyfQ0Kbl9zaXplID0gNTANCm5zYW1wbGUgPC0gc2FtcGxlX24odHJhaW4sc2l6ZT1uX3NpemUscmVwbGFjZT1UKQ0KDQojIENhbGN1bGF0ZSBUaGUgTWVhbg0KbWVhbl9lc3QyPW1lYW4obnNhbXBsZSRBZ2UsbmEucm0gPSBUKQ0KDQojIENhbGN1bGF0ZSBUaGUgU3RhbmRhcmQgRGV2aWF0aW9uDQpzZF9lc3QyPXNkKG5zYW1wbGUkQWdlLG5hLnJtID0gVCkNCg0KIyBDb21wdXRpbmcgVGhlIEVycm9yIFVzaW5nIHRoZSBxbm9ybSgpIEZ1bmN0aW9uIHRvIENhbGN1bGF0ZSBUaGUgTm9ybWFsIERpc3RyaWJ1dGlvbiANCmVycm9yPXFub3JtKDAuOTc1KSooc2RfZXN0MS9zcXJ0KG5fc2l6ZSkpDQoNCiNEZXRlcm1pbmluZyBUaGUgTWVhbiBJbnRlcnZhbFtdDQpsZWZ0IDwtIG1lYW5fZXN0Mi1lcnJvcg0KcmlnaHQgPC0gbWVhbl9lc3QyK2Vycm9yDQoNCnByaW50KHBhc3RlMCgiWyIsbGVmdCwiLCIscmlnaHQsIl0iKSkNCmBgYA0KIyMgTXVsdGlwbGljYXRpb24gYW5kIGFkZGl0aW9uIG9mIGNvbnN0YW50IHRvIHRoZSBzYW1wbGUgdmFsdWVzDQoNCiMjIyBNdWx0aXBsaWNhdGlvbg0KDQpgYGB7cn0NCm5fc2l6ZSA9IDIwMA0KDQpuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemUgPSBuX3NpemUscmVwbGFjZSA9IFQpDQoNCm5zYW1wbGUgJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlKSwgQWdlX3NkID0gc2QoQWdlKSxBZ2VfdmFyID0gc2QoQWdlKSoqMikNCg0KbnNhbXBsZSRBZ2UgPC0gbnNhbXBsZSRBZ2UgKjUNCm5zYW1wbGUgJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlKSwgQWdlX3NkID0gc2QoQWdlKSxBZ2VfdmFyID0gc2QoQWdlKSoqMikNCg0KYGBgDQojIyMgQWRkaXRpb24NCg0KYGBge3J9DQpuX3NpemUgPSAyMDANCg0KbnNhbXBsZSA8LSBzYW1wbGVfbih0cmFpbixzaXplID0gbl9zaXplLHJlcGxhY2UgPSBUKQ0KDQpuc2FtcGxlICU+JQ0KICBzdW1tYXJpc2UoQWdlX21lYW4gPSBtZWFuKEFnZSksIEFnZV9zZCA9IHNkKEFnZSksQWdlX3ZhciA9IHNkKEFnZSkqKjIpDQoNCm5zYW1wbGUkQWdlIDwtIG5zYW1wbGUkQWdlICsgNQ0KbnNhbXBsZSAlPiUNCiAgc3VtbWFyaXNlKEFnZV9tZWFuID0gbWVhbihBZ2UpLCBBZ2Vfc2QgPSBzZChBZ2UpLEFnZV92YXIgPSBzZChBZ2UpKioyKQ0KDQpgYGANCg0KIyBrZXJuZWwgZGlzdHJpYnV0aW9uDQoNCj4gQSBrZXJuZWwgZGlzdHJpYnV0aW9uIGlzIGEgbm9ucGFyYW1ldHJpYyByZXByZXNlbnRhdGlvbiBvZiB0aGUNCj4gcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiAoJHBkZiQpIG9mIGEgcmFuZG9tIHZhcmlhYmxlIGluIGFueQ0KPiBwb3B1bGF0aW9uDQoNClRoZSBrZXJuZWwgc21vb3RoaW5nIGZ1bmN0aW9uIGRlZmluZXMgdGhlIHNoYXBlIG9mIHRoZSBjdXJ2ZSB1c2VkIHRvDQpnZW5lcmF0ZSB0aGUgcGRmIEtlcm5lbCBkaXN0cmlidXRpb24gaXMgUXVvdGUgZnJvbSBoaXN0b2dyYW0gaW4gb3RoZXINCndvcmQgKHNtb290aCByZXByZXNlbnRhdGlvbiBvZiBhIGhpc3RvZ3JhbSkgVGhhdCB0aGUgaW50ZWdyYWwgPTEgVGhlcmUNCmlzIGEgYmVuZWZpdCBvZiBzbW9vdGggcmVwcmVzZW50YXRpb24gb2YgYSBoaXN0b2dyYW0gbGlrZSBJZ25vcmVzDQppcnJlZ3VsYXJpdGllcyBhbmQgb3V0bGllcnMgLCBtb3JlIGVmZmljaWVudCBpbiBhcHByb3hpbWF0aW9uIHNvIGl0DQpkZWFscyBiZXR0ZXIgd2l0aCBsYXJnZSBkYXRhIHRoYW4gc21hbGwgZGF0YQ0KDQokJCBcaGF0e2ZfaH0gPSBcZnJhY3sxfXtufSA9IFxzdW1ebl97aSA9IDF9IEsoeC14X2kpICA9IFxmcmFjezF9e25ofSBLXGxlZnQoXGZyYWN7eC14X2l9e2h9XHJpZ2h0KSAkJA0KDQojIyBSdWxlcw0KDQojIyMgTm9uLXdlaWdodGVkIERhdGENCg0KJCQgXGhhdHtmX2h9ID0gXGZyYWN7MX17bn0gXHN1bV5uX3tpID0gMX0gSyh4LXhfaSkgID0gXGZyYWN7MX17bmh9IEtcbGVmdChcZnJhY3t4LXhfaX17aH1ccmlnaHQpICQkDQoNCiMjIyBXZWlnaHRlZCBEYXRhDQoNCiQkDQpcaGF0e2ZfaH0gPSBcZnJhY3sxfXtofSBcc3VtXk5fe2k9MX0gd19pIEsgXGxlZnQoXGZyYWN7eC14X2l9e2h9XHJpZ2h0KSwgXHFxdWFkIFx0ZXh0e3doZXJlfSBcc3VtXk5fe2k9IDF9IHdfaSAgPSAxDQokJA0KDQojIyBLZXJuZWwgRnVuY3Rpb24NCg0KMS4gQm94DQoyLiBUcmlhbmdsZQ0KMy4gTm9ybWFsDQo0LiBQYW50ZWNobmljb24NCg0KRWFjaCBkZW5zaXR5IGN1cnZlIHVzZXMgdGhlIHNhbWUgaW5wdXQgZGF0YSwgYnV0IGFwcGxpZXMgYSBkaWZmZXJlbnQNCmtlcm5lbCBzbW9vdGhpbmcgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgdGhlIHBkZi4gVGhlIGRlbnNpdHkgZXN0aW1hdGVzIGFyZQ0Kcm91Z2hseSBjb21wYXJhYmxlLCBidXQgdGhlIHNoYXBlIG9mIGVhY2ggY3VydmUgdmFyaWVzIHNsaWdodGx5LiBGb3INCmV4YW1wbGUsIHRoZSBib3gga2VybmVsIHByb2R1Y2VzIGEgZGVuc2l0eSBjdXJ2ZSB0aGF0IGlzIGxlc3Mgc21vb3RoDQp0aGFuIHRoZSBvdGhlcnMuDQoNClRoZSBjaG9pY2Ugb2YgYmFuZHdpZHRoIHZhbHVlIGNvbnRyb2xzIHRoZSBzbW9vdGhuZXNzIG9mIHRoZSByZXN1bHRpbmcNCnByb2JhYmlsaXR5IGRlbnNpdHkgY3VydmUgKGhpZ2hlciB2YWx1ZSBvZiAkaCQgbW9yZSBzbW9vdGhpbmcpDQoNClNwZWNpZnlpbmcgYSBzbWFsbGVyIGJhbmR3aWR0aCBwcm9kdWNlcyBhIHZlcnkgcm91Z2ggY3VydmUsIGJ1dCByZXZlYWxzDQp0aGF0IHRoZXJlIG1pZ2h0IGJlIHR3byBtYWpvciBwZWFrcyBpbiB0aGUgZGF0YS4gU3BlY2lmeWluZyBhIGxhcmdlcg0KYmFuZHdpZHRoIHByb2R1Y2VzIGEgY3VydmUgbmVhcmx5IGlkZW50aWNhbCB0byB0aGUga2VybmVsIGZ1bmN0aW9uDQpDaG9vc2luZyB0aGUgb3B0aW1hbCAoJGgkKSBiYW5kd2lkdGggbWV0aG9kcyA6DQoNCjEuIFNpbHZlcm1hbidzIHJ1bGUgb2YgdGh1bXAgdGhhdCBjb21wdXRlcyBhbiBvcHRpbWFsIGggYnkgYXNzdW1pbmcNCiAgICB0aGF0IGRhdGEgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCjIuIEltcHJvdmVkIFNoZWF0aGVyIEpvbmVzIChJU0opIGFuIGFsZ29yaXRobSBpcyBtb3JlIHJvYnVzdCB3aXRoDQogICAgbXVsdGltb2RhbGl0eSBkYXRhIG9yIGEgbG90IG9mIGRhdGEgKG9uZSBkaXNhZHZhbnRhZ2UgaXMgaXQgbmVlZHMgdG8NCiAgICBsYXJnZSBkYXRhICkNCg0KQm91bmRlZCBkb21haW5zIGRhdGE6IGhhdmUgYSBjb25zdHJhaW5zIGxpa2UgZGF0YSBjb3VsZG4ndCBiZSBuZWdhdGl2ZSAoDQotdmUgbGVhZCB0byBwcm9iYWJpbGl0eSA9IDApDQoNCiMjIE1pcnJvciBtZXRob2QNCg0KMS4gIE1pcnJvciB0aGUgZGF0YQ0KMi4gIFN1bSB0aGUgb3JpZ2luYWwgYW5kIG1pcnJvcmVkIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlDQozLiAgQ2hvcCBpdCBzbyB0aGF0IHplcm8gYXQgdGhlIGJvdW5kYXJ5IHNpZGUNCg0KIyMgMkQNCg0KJGgkIDogY291bGQgYmUgbWF0cml4IChkaWZmZXJlbnQgJGgkIGluIGRpZmZlcmVudCBkaXJlY3Rpb25zKQ0KDQpUaGUgY2hvaWNlIG9mIG5vcm0gY29tZXMgaW50byAkZCBcZ2UgMiQNCg0KVGhlIHAtbm9ybSBpcyAkfHx4fHxfcCA6PSAoXHN1bV97aT0xfSB8eHxecClee1xmcmFjezF9e3B9fSQgLSBub3JtLXAgPTENCk1hbmhhdHRhbiBkaXN0YW5jZSAtIG5vcm0tcCA9MiBFdWNsaWRlYW4gbm9ybSAtIG5vcm0tcCA9aW5mIG1heGltdW0gbm9ybQ0KKGl0J3Mgbm90IG9idmlvdXMgaW4gZXZlcnkgY2FzZSB3aGljaCBub3JtIGlzIHRoZSBjb3JyZWN0IG9uZSkNCg0Kc3RhbmRhcmQgZXVjbGlkZWFuIGRpc3RhbmNlIGlzIGdvb2QgY2hvaWNlIGJlY2F1c2UgaXQgaW52YXJpYW50IHVuZGVyDQpyb3RhdGlvbiBhcyBsYXJnZSBkYXRhIGNob2ljZSBvZiBrIGFuZCBwIGlzbid0IGltcG9ydGFudCBzbw0KDQojIEtvbG1vZ29yb3YtU21pcm5vdiBUZXN0DQoNCj4gTm9uIHBhcmFtZXRyaWMgdGVzdA0KDQpLb2xtb2dvcm92LVNtaXJub3YgVGVzdCBpcyB1c2VkIHRvOg0KMS4gRGVjaWRlIGlmIGEgc2FtcGxlIGNvbWVzIGZyb20gYSBwb3B1bGF0aW9uIHdpdGggYW4gZXhwZWN0ZWQgY29udGludW91cyBkaXN0cmlidXRpb24gKG1vc3RseSBub3JtYWwgZGlzdHJpYnV0aW9uKQ0KMi4gVG8gdGVzdCBmb3IgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIHNoYXBlIG9mIHR3byBzYW1wbGUgZGlzdHJpYnV0aW9ucw0KDQohW10oaW1nL2tzLTEucG5nKQ0KDQpDb21wYXJlIG92ZXJhbGwgc2hhcGUgb2YgZGlzdHJpYnV0aW9uLCBub3Qgc3BlY2lmaWNhbGx5IHBhcmFtZXRlcg0KDQpLb2xtb2dvcm92LVNtaXJub3YgVGVzdCBpcyBkZWZpbmVkIGJ5IGEgaHlwb3RoZXNpczogDQokSF8wJCA6IHRoZSBkYXRhIGZvbGxvdyBzcGVjaWZpYyBkaXN0cmlidXRpb24gJEYoeCkgPSBGX1QgKHgpJA0KJEhfYSQgOiB0aGUgZGF0YSBkb24ndCBmb2xsb3cgc3BlY2lmaWMgZGlzdHJpYnV0aW9uICAkRih4KSBcbmVxIEZfVCAoeCkkDQoNCktTLXRlc3QgaXMgbWFkZSBiZXR3ZWVuIHNvbWUgdGhlb3JldGljYWwgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKCRGX1QoeCkkKSwgYW5kIGEgc2FtcGxlIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uICgkRl9zKHgpJCkgdGhhdCBtZWFzdXJlZCBieSB0aGUgc3RhdGlzdGljIEQsIHdoaWNoIGlzIHRoZSBncmVhdGVzdCB2ZXJ0aWNhbCBkaXN0YW5jZSBiZXR3ZWVuIHRoZW0uDQokJCBEID0gXHVuZGVyYnJhY2V7XHRleHR7c3VwfX1fe3h9IHwgRl9zKHgpIC0gRl90ICh4KXwkJA0KIVtdKGltZy9rcy0yLnBuZykNCg0KMS4gRGV0ZXJtaW5lICR4JCB2YWx1ZXMNCjIuIERldGVtcmluZSBmcmVxdWVuY3kgb2YgZWFjaCBvYnNlcnZhdGlvbg0KMy4gQ2FsY3VsYXRlIGN1bXVsYXRpdmUgZnJlcXVlbmN5DQo0LiBDYWxjdWxhdGUgJEZfcyh4KSBccmlnaHRhcnJvdyBcZnJhY3tcdGV4dHtjdW11bGF0aXZlIGZyZXF1ZW5jZX19e059JCB3aGVuICRuID4gMzAkICgkeiQtc2NvcmVzKSwgd2hlbiAkbjwzMCQgKCR0JC1zY29yZXMpDQo1LiBjYWxjdWxhdGUgJEZfdCh4KSQNCjYuIENhbGN1bGF0ZSAkRCBccmlnaHRhcnJvdyBGX3MoeCkgLSBGX1QoeCkkDQo3LiBDaG9vc2UgbWF4aW11bSAkRCQNCjguIENhbGN1bGF0ZSAkUCQtdmFsdWUNCjkuIERldGVybWluZSBLb2xtb2dvcm92J3MgcXVhcnRpbGUNCjEwLiBDb21wYXJlICRwJC12YWx1ZSB3aXRoIEtvbG1vZ29yb3ZlJ3MgcXVhcnRpbGUNCg==